package com.clp.protocol.iec104.iec104.server.slavechannel.pipeline.state.data;

import com.clp.protocol.iec104.iec104.apdu.asdu.Cot;
import com.clp.protocol.iec104.iec104.apdu.asdu.IAsdu;
import com.clp.protocol.iec104.iec104.apdu.asdu.infoobj.CInfoObj;
import com.clp.protocol.iec104.iec104.apdu.asdu.infoobj.InfoObj;
import com.clp.protocol.iec104.iec104.definition.TypeTag;
import com.clp.protocol.iec104.iec104.definition.cot.Cause;
import com.clp.protocol.iec104.iec104.definition.cot.Pn;
import com.clp.protocol.iec104.iec104.server.slave.SlaveDataConfig;
import com.clp.protocol.iec104.iec104.server.slavechannel.pipeline.PipelineManager;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelPromise;
import lombok.extern.slf4j.Slf4j;

import javax.annotation.Nullable;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.function.BiConsumer;

/**
 * 总召唤100代码功能实现
 */
@Slf4j
public class TotalCall100DataStateHandler extends AbstractDataStateHandler {

    private enum State {
        /**
         * 新请求到达
         */
        R_NEW_REQ,
        /**
         * 总召唤肯定确认
         */
        S_ACK_YES,
        /**
         * 总召唤否定确认
         */
        S_ACK_NO,
        /**
         * 正在传输遥测数据
         */
        S_TM_TRANSMITTING,
        /**
         * 正在传输遥信数据
         */
        S_TS_TRANSMITTING,
        /**
         * 总召唤终止
         */
        FINISHED;
    }

    private volatile State state;
    private final TotalCall100DataReactor dataReactor;

    public TotalCall100DataStateHandler(PipelineManager manager, SlaveDataConfig cfg) {
        super(manager);
        this.dataReactor = cfg.getTotalCall100DataReactor();
    }

    @Override
    protected void resetState() {
        this.state = State.FINISHED;
    }


    @Override
    protected void afterResetState() {

    }

    private void checkStateIn(State... states) {
        for (State stateToCheck : states) {
            if (state == stateToCheck) return;
        }
        throw new IllegalStateException("当前状态：" + state + "，需要状态：" + Arrays.toString(states));
    }

    private void setState(State stateToSet) {
        state = stateToSet;
    }

    @Override
    protected IAsdu updateStateByRecv(IAsdu iAsdu) throws Exception {
        TypeTag typeTag = iAsdu.getTypeTag();
        Cot cot = iAsdu.getCot();
        Cause cause = cot.getCause();
        if (typeTag == TypeTag.C_IC_NA_1) {
            if (cause == Cause.COT_ACT) {
                // 总召唤激活
                checkStateIn(State.FINISHED); // 确认当前没有进行总召唤
                setState(State.R_NEW_REQ); // 设置接收到新的总召唤
                CompletableFuture<Boolean> acceptFuture = dataReactor.accept(inSlaveChannel());
                acceptFuture.whenComplete(new BiConsumer<Boolean, Throwable>() {
                    @Override
                    public void accept(Boolean isAccepted, Throwable cause) {
                        if (cause != null) {
                            cause.printStackTrace();
                            return;
                        }
                        if (isAccepted) {
                            // 发送总召唤肯定确认
                            iAsduSender().chSendTotalCall100Ack(Pn.PN_YES).addListener(new ChannelFutureListener() {
                                @Override
                                public void operationComplete(ChannelFuture future) throws Exception {
                                    if (!future.isSuccess()) return;

                                    // 肯定确认之后需要开始依次发送遥测、遥信报文
                                    sendTmData().addListener(new ChannelFutureListener() {
                                        @Override
                                        public void operationComplete(ChannelFuture future) throws Exception {
                                            if (!future.isSuccess()) {
                                                future.cause().printStackTrace();
                                                return;
                                            }

                                            log.info("[TotalCall100] 总召唤100-遥测响应报文发送结束");

                                            // 遥测发送结束之后，发送遥信报文
                                            sendTsData().addListener(new ChannelFutureListener() {
                                                @Override
                                                public void operationComplete(ChannelFuture future) throws Exception {
                                                    if (!future.isSuccess()) {
                                                        future.cause().printStackTrace();
                                                        return;
                                                    }

                                                    log.info("[TotalCall100] 总召唤100-遥信响应报文发送结束");

                                                    // 遥信发送结束之后，发送终止报文
                                                    iAsduSender().chSendTotalCall100Finished().addListener(new ChannelFutureListener() {
                                                        @Override
                                                        public void operationComplete(ChannelFuture future) throws Exception {
                                                            if (!future.isSuccess()) {
                                                                future.cause().printStackTrace();
                                                                return;
                                                            }

                                                            log.info("[TotalCall100] 总召唤100-已终止");
                                                        }
                                                    });
                                                }
                                            });
                                        }
                                    });
                                }
                            });
                        } else {
                            // 发送总召唤否定确认
                            iAsduSender().chSendTotalCall100Ack(Pn.PN_NO);
                        }
                    }
                });
            }
        }

        return iAsdu;
    }

    /**
     * 发送遥测报文
     * @return
     */
    private ChannelFuture sendTmData() {
        ChannelPromise sendPromise = channel.newPromise();
        dataReactor.collectTmData(inSlaveChannel()).whenComplete(new BiConsumer<List<TmData>, Throwable>() {
            @Override
            public void accept(List<TmData> tmData, Throwable throwable) {
                if (throwable != null) {
                    throw new RuntimeException(throwable);
                }
                if (tmData == null) {
                    throw new NullPointerException();
                }
                // 如果数据为空，则直接发送成功
                if (tmData.isEmpty()) {
                    sendPromise.setSuccess();
                }

                List<List<InfoObj>> infoObjsList = new LinkedList<>(); // 待发送的asdu集合

                TotalCall100DataReactor.TmDataType tmDataType = dataReactor.getTmDataType();
                int maxCount = tmDataType.getMaxCount(); // 单个asdu可用最大数量
                // 构造apdu并发送
                // 进行排序
                tmData.sort(TmData.Comparator.INSTANCE);
                // 对于信息体地址缺失的，使用无效位代替
                int nextAddress = tmData.get(0).getAddress(); // 起始地址
                ListIterator<TmData> iter = tmData.listIterator();
                List<InfoObj> infoObjs = new ArrayList<>(maxCount);
                int currCount = 0;
                while (iter.hasNext()) {
                    CInfoObj cInfoObj = tmDataType.convert(iter.next());
                    int addr = cInfoObj.getAddr();

                    if (currCount == maxCount) {
                        // 达到最大值
                        infoObjsList.add(infoObjs);
                        infoObjs = new ArrayList<>(maxCount);
                        currCount = 0;
                    }

                    while (nextAddress < addr) {
                        if (currCount == maxCount) {
                            // 达到最大值
                            infoObjsList.add(infoObjs);
                            infoObjs = new ArrayList<>(maxCount);
                            currCount = 0;
                        }
                        CInfoObj invalidCInfoObj = tmDataType.convertInvalid(nextAddress);
                        infoObjs.add(invalidCInfoObj);
                        nextAddress++;
                        currCount++;
                    }

                    if (currCount == maxCount) {
                        // 达到最大值
                        infoObjsList.add(infoObjs);
                        infoObjs = new ArrayList<>(maxCount);
                        currCount = 0;
                    }

                    infoObjs.add(cInfoObj);
                    nextAddress++;
                    currCount++;
                }
                if (!infoObjs.isEmpty()) {
                    infoObjsList.add(infoObjs);
                }

                // 一个一个发送
                sendTmInfoObj(tmDataType, infoObjsList.listIterator(), sendPromise);
            }
        });

        return sendPromise;
    }

    private void sendTmInfoObj(TotalCall100DataReactor.TmDataType tmDataType, Iterator<List<InfoObj>> iter, ChannelPromise promise) {
        if (!iter.hasNext()) {
            promise.setSuccess(); // 全部发送完毕了，就表示发送成功
            return;
        }
        iAsduSender().chSendTotalCall100TmData(tmDataType, iter.next()).addListener(new ChannelFutureListener() {
            @Override
            public void operationComplete(ChannelFuture future) throws Exception {
                if (!future.isSuccess()) {
                    promise.setFailure(future.cause());
                    return;
                }
                sendTmInfoObj(tmDataType, iter, promise);
            }
        });
    }

    private ChannelFuture sendTsData() {
        ChannelPromise sendPromise = channel.newPromise();
        dataReactor.collectTsData(inSlaveChannel()).whenComplete(new BiConsumer<List<TsData>, Throwable>() {
            @Override
            public void accept(List<TsData> tsData, Throwable throwable) {
                if (throwable != null) {
                    throw new RuntimeException(throwable);
                }
                if (tsData == null) {
                    throw new NullPointerException();
                }
                // 如果数据为空，则直接发送成功
                if (tsData.isEmpty()) {
                    sendPromise.setSuccess();
                }

                List<List<InfoObj>> infoObjsList = new LinkedList<>(); // 待发送的asdu集合

                TotalCall100DataReactor.TsDataType tsDataType = dataReactor.getTsDataType();
                int maxCount = tsDataType.getMaxCount(); // 单个asdu可用最大数量
                // 构造apdu并发送
                // 进行排序
                tsData.sort(TsData.Comparator.INSTANCE);
                // 对于信息体地址缺失的，使用无效位代替
                int nextAddress = tsData.get(0).getAddress(); // 起始地址
                ListIterator<TsData> iter = tsData.listIterator();
                List<InfoObj> infoObjs = new ArrayList<>(maxCount);
                int currCount = 0;
                while (iter.hasNext()) {
                    CInfoObj cInfoObj = tsDataType.convert(iter.next());
                    int addr = cInfoObj.getAddr();

                    if (currCount == maxCount) {
                        // 达到最大值
                        infoObjsList.add(infoObjs);
                        infoObjs = new ArrayList<>(maxCount);
                        currCount = 0;
                    }

                    while (nextAddress < addr) {
                        if (currCount == maxCount) {
                            // 达到最大值
                            infoObjsList.add(infoObjs);
                            infoObjs = new ArrayList<>(maxCount);
                            currCount = 0;
                        }
                        CInfoObj invalidCInfoObj = tsDataType.convertInvalid(nextAddress);
                        infoObjs.add(invalidCInfoObj);
                        nextAddress++;
                        currCount++;
                    }

                    if (currCount == maxCount) {
                        // 达到最大值
                        infoObjsList.add(infoObjs);
                        infoObjs = new ArrayList<>(maxCount);
                        currCount = 0;
                    }

                    infoObjs.add(cInfoObj);
                    nextAddress++;
                    currCount++;
                }
                if (!infoObjs.isEmpty()) {
                    infoObjsList.add(infoObjs);
                }

                // 一个一个发送
                sendTsInfoObj(tsDataType, infoObjsList.listIterator(), sendPromise);
            }
        });

        return sendPromise;
    }

    private void sendTsInfoObj(TotalCall100DataReactor.TsDataType tsDataType, ListIterator<List<InfoObj>> iter, ChannelPromise promise) {
        if (!iter.hasNext()) {
            promise.setSuccess(); // 全部发送完毕了，就表示发送成功
            return;
        }
        iAsduSender().chSendTotalCall100TsData(tsDataType, iter.next()).addListener(new ChannelFutureListener() {
            @Override
            public void operationComplete(ChannelFuture future) throws Exception {
                if (!future.isSuccess()) {
                    promise.setFailure(future.cause());
                    return;
                }
                sendTsInfoObj(tsDataType, iter, promise);
            }
        });
    }

    @Override
    protected IAsdu updateStateBySend(IAsdu iAsdu) {
        TypeTag typeTag = iAsdu.getTypeTag();
        Cot cot = iAsdu.getCot();
        Cause cause = cot.getCause();
        Pn pn = cot.getPn();

        if (typeTag == TypeTag.C_IC_NA_1) { // 总召唤
            if (cause == Cause.COT_ACTCON) {
                // 检查状态
                checkStateIn(State.R_NEW_REQ);
                // 激活确认
                if (pn == Pn.PN_YES) {
                    // 肯定确认
                    setState(State.S_ACK_YES);
                } else {
                    // 否定确认
                    setState(State.S_ACK_NO);
                    // 恢复为结束状态
                    setState(State.FINISHED);
                }
            } else if (cause == Cause.COT_ACTTERM) {
                // 激活终止
                if (pn == Pn.PN_YES) {

                } else {

                }
            }
        } else if (typeTag == TypeTag.M_ME_NC_1) { // 短浮点数
            if (cause == Cause.COT_INRO) { // 响应站召唤
                checkStateIn(State.S_ACK_YES, State.S_TM_TRANSMITTING); // 前提条件：肯定确认
                setState(State.S_TM_TRANSMITTING); // 表示进行遥测传输
            }
        } else if (typeTag == TypeTag.M_SP_NA_1 || typeTag == TypeTag.M_DP_NA_1) { // 单点信息或双点信息
            if (cause == Cause.COT_INRO) { // 响应站召唤
//                checkStateIn();
            }
        }

        return iAsdu;
    }

    @Nullable
    @Override
    protected ScheduledTask getScheduledTask() {
        return null;
    }
}
